package org.budo.graph.service.util;

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import org.budo.config.client.spring.BudoConfigPropertySourcesPlaceholderConfigurer;
import org.budo.graph.Constant;
import org.budo.support.config.processor.AbstractBudoConfigProcessor;
import org.budo.support.freemarker.FreemarkerUtil;
import org.budo.support.lang.util.MapUtil;
import org.budo.support.lang.util.ReflectUtil;
import org.budo.support.slf4j.Slf4j;
import org.budo.support.spring.io.util.ResourceUtil;
import org.budo.support.spring.util.SpringUtil;
import org.slf4j.Logger;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.PropertySources;
import org.springframework.jndi.JndiLocatorDelegate;
import org.springframework.web.context.support.StandardServletEnvironment;

/**
 * @author limingwei
 */
public class PlaceholderUtil {
    private static final Logger log = Slf4j.getLogger();

    private static final String DUBBO_DRUID_FM_PLACEHOLDER_HTM = Constant.GRAPH_TEMPLATE_PATH + "/placeholder.htm";

    private static final String PLACEHOLDERS = "placeholders";

    public static String writePlaceholderResponse(ApplicationContext applicationContext, String url) {
        Map resultMap = new HashMap();

        Map contextPropertyMap = getContextPropertyMap(applicationContext, url);
        MapUtil.putAll(resultMap, contextPropertyMap);

        Map configProcessorMap = getConfigProcessorMap(applicationContext);
        MapUtil.putAll(resultMap, configProcessorMap);

        Map propertiesLoaderSupportMap = getPropertiesLoaderSupportMap(applicationContext);
        MapUtil.putAll(resultMap, propertiesLoaderSupportMap);
        for (Object key : resultMap.keySet()) {
            if (null != key) {
                String _key = key.toString().toLowerCase();
                if (_key.contains("password") // 密码脱敏
                        || _key.contains("pwd") //
                        || _key.contains("secret") //
                        || _key.contains("token") //
                        || _key.contains("sign")) {
                    resultMap.put(key, "***");
                }
            }
        }

        String inputString = ResourceUtil.classPathResourceInputStreamToString(DUBBO_DRUID_FM_PLACEHOLDER_HTM);
        return FreemarkerUtil.freemarkerRender(inputString, MapUtil.stringObjectMap(PLACEHOLDERS, resultMap));
    }

    private static Map getPropertiesLoaderSupportMap(ApplicationContext applicationContext) {
        Map map = new HashMap();
        String[] beanNames = applicationContext.getBeanNamesForType(PropertySourcesPlaceholderConfigurer.class);
        for (String beanName : beanNames) {
            PropertySourcesPlaceholderConfigurer placeholderConfigurer = (PropertySourcesPlaceholderConfigurer) applicationContext.getBean(beanName);
            PropertySources appliedPropertySources = placeholderConfigurer.getAppliedPropertySources();

            Map findProperties = findProperties(appliedPropertySources);
            map.putAll(findProperties);
        }

        return map;
    }

    private static Map findProperties(Object source) {
        if (null == source //
                || source.getClass().equals(Object.class) //
                || source instanceof StandardServletEnvironment) {
            return new HashMap();
        }

        if (source instanceof Map) {
            return (Map) source;
        }

        if (source instanceof AbstractEnvironment) {
            AbstractEnvironment abstractEnvironment = (AbstractEnvironment) source;
            MutablePropertySources mutablePropertySources = abstractEnvironment.getPropertySources();
            Map findProperties = findProperties(mutablePropertySources);
            return findProperties;
        }

        if (source instanceof PropertySources) {
            PropertySources propertySources = (PropertySources) source;
            Map map = new HashMap();
            for (PropertySource<?> propertySource : propertySources) {
                Object source2 = propertySource.getSource();
                Map findProperties = findProperties(source2);
                MapUtil.putAll(map, findProperties);
            }
            return map;
        }

        if (source instanceof JndiLocatorDelegate) {
            Properties jndiEnvironment = ((JndiLocatorDelegate) source).getJndiEnvironment();
            return jndiEnvironment;
        }

        log.warn("#105 source.type=" + source.getClass() + ", source=" + source);
        return new HashMap();
    }

    private static Map getConfigProcessorMap(ApplicationContext applicationContext) {
        AbstractBudoConfigProcessor configProcessor = SpringUtil.getBean(applicationContext, AbstractBudoConfigProcessor.class);
        if (null == configProcessor) {
            return MapUtil.stringObjectMap("budo.config_processor_is_null", "1");
        }

        Properties properties = configProcessor.getProperties();
        if (null == properties) {
            return MapUtil.stringObjectMap("budo.config_processor_properties_is_null", "1");
        }

        if (properties.isEmpty()) {
            return MapUtil.stringObjectMap("budo.config_processor_properties_is_empty", "1");
        }

        return properties;
    }

    private static Map getContextPropertyMap(ApplicationContext applicationContext, String url) {
        if (!ReflectUtil.hasClass("org.budo.config.client.spring.BudoConfigPropertySourcesPlaceholderConfigurer")) {
            log.error("#70 no BudoConfigPropertySourcesPlaceholderConfigurer");
            return new HashMap<String, Object>();
        }

        try {
            BudoConfigPropertySourcesPlaceholderConfigurer configurer = applicationContext.getBean(BudoConfigPropertySourcesPlaceholderConfigurer.class);
            Properties mergedProperties = configurer.getMergedProperties();
            return mergedProperties;
        } catch (Throwable e) {
            log.error("#81 getContextPropertyMap error", e);
            return new HashMap();
        }
    }
}